/*
 * main.c
 *
 *  Created on: Apr 20, 2011
 *      Author: mariano
 */
#include "../include/defs.h"
#include "../include/parsers.h"
#include "../include/element.h"
#include "../include/array.h"

#include <string.h>
#include <stdio.h>

Array * getElementsDoneUsing(char * root, Array * magics) {
	int counter = 0;
	Array * magicSelected = getElementsByFunction(magics, (int(*)(void *,
			void *)) isReactor, newElement(root));
	Array * out = transformArray(magicSelected,
			(void *(*)(void *)) getMagicProduct);
	deleteRepetedElementsFormArray(out, (int(*)(void *, void *)) elementCmp);
	deleteArray(magicSelected);
	while (counter < out->count) {
		magicSelected = getElementsByFunction(magics,
				(int(*)(void *, void *)) isReactor, getElementByIndex(out,
						counter));
		Array * step = transformArray(magicSelected,
				(void *(*)(void *)) getMagicProduct);
		deleteArray(magicSelected);
		deleteRepetedElementsFormArray(step,
				(int(*)(void *, void *)) elementCmp);
		int i;
		for (i = 0; i < out->count; i++) {
			deleteElementFromArray(step, getElementByIndex(out, i), (int(*)(
					void *, void *)) elementCmp);
		}
		for (i = 0; i < step->count; i++) {
			add(out, getElementByIndex(step, i));
		}
		counter++;
		deleteArray(step);
	}
	return out;
}

Array * getBasicsFor(char * product, Array * magics) {
	int counter = 0;
	Array * elements = newArray();
	add(elements, newElement(product));
	while (counter < elements->count) {
		Array * magicSelected = getElementsByFunction(magics, (int(*)(void *,
				void *)) isProduct, getElementByIndex(elements, counter));
		Array * step1 = transformArray(magicSelected,
				(void *(*)(void *)) getMagicFirstReactor);
		Array * step2 = transformArray(magicSelected,
				(void *(*)(void *)) getMagicSecondReactor);
		deleteArray(magicSelected);
		deleteRepetedElementsFormArray(step1,
			(int(*)(void *, void *)) elementCmp);
		deleteRepetedElementsFormArray(step2,
			(int(*)(void *, void *)) elementCmp);
		int i;
		for (i = 0; i < elements->count; i++) {
			deleteElementFromArray(step1, getElementByIndex(elements, i),
					(int(*)(void *, void *)) elementCmp);
			deleteElementFromArray(step2, getElementByIndex(elements, i),
					(int(*)(void *, void *)) elementCmp);
		}
		for (i = 0; i < step2->count; i++) {
			deleteElementFromArray(step1, getElementByIndex(step2, i), (int(*)(
					void *, void *)) elementCmp);
		}
		for (i = 0; i < step1->count; i++) {
			add(elements, getElementByIndex(step1, i));
		}
		for (i = 0; i < step2->count; i++) {
			add(elements, getElementByIndex(step2, i));
		}
		counter++;
		deleteArray(step1);
		deleteArray(step2);
	}
	Array * out = getElementsByFunction(elements,
			(int(*)(void *, void *)) isBasic, magics);
	return out;
}

char * strnormalise(char * str){
	int j, i;
	// cambia enters por espacios
	for(j=0;str[j]!='\0';j++){
		if(str[j]=='\n' || str[j] == '\t'){
			str[j]=' ';
		}
	}
	// elimina espacios del principio
	while(str[0]==' '){
		str=str+1;
	}
	//elimina espacios del final
	for(i=strlen(str)-1;i>0 && str[i]==' ';i--){
		str[i]='\0';
	}
	//elimina espacios repetidos en el medio
	for(j=0;str[j]!='\0';j++){
		if(str[j]==' ' && str[j+1]==' '){
			strcpy(str + j, str + j + 1);
			j--;
		}
	}
	return str;
}

int main(int argc, char *argv[]) {

	Array * arguments = newArray();

	int i;
	for (i = 1; i < argc; i++)
		add(arguments, argv[i]);

	char * elementFile = NULL;
	char * magicFile = NULL;

	bool helpDialog = false;

	bool rootElement = false;
	char * rootElementARG = NULL;

	bool terminalElements = false;

	bool possibleCombinations = false;
	char * possibleCombinationsARG = NULL;

	// Parse arguments FIRST
	for (i = 0; i < arguments->count; i++) {
		if (strcmp(arguments->array[i], "-n") == 0){
			elementFile = arguments->array[i + 1];
			if(elementFile != NULL)
				elementFile = strnormalise(elementFile);
		}
		
		if (strcmp(arguments->array[i], "-m") == 0){
			magicFile = arguments->array[i + 1];
			if(magicFile!= NULL)
				magicFile = strnormalise(magicFile);
		}

		if (strcmp(arguments->array[i], "-t") == 0)
			terminalElements = true;

		if (strcmp(arguments->array[i], "-h") == 0)
			helpDialog = true;

		if (strcmp(arguments->array[i], "-e") == 0) {
			rootElement = true;
			rootElementARG = arguments->array[i + 1];
			if(rootElementARG!=NULL)
				rootElementARG = strnormalise(rootElementARG);
			//if(rootElementARG[0]=='"'){
			//	for(j=i+2;i<arguments->count && strchr(arguments->array[j],'"')==NULL;j++){
			//		rootElementARG=strcat(rootElementARG,strcat(" ",arguments->array[j]));
			//	}
			//	*strrchr(rootElementARG,'"')='\0';
			//	rootElementARG=strchr(rootElementARG,'"');
			//}
		}

		if (strcmp(arguments->array[i], "-b") == 0) {
			possibleCombinations = true;
			possibleCombinationsARG = arguments->array[i + 1];
			if(possibleCombinationsARG != NULL)
				possibleCombinationsARG = strnormalise(possibleCombinationsARG);
		}

	}

	//	printf("\nElementos:\n");
	Array * elements = loadElements(elementFile);
	if (elements == NULL) {
		printf("\nNo such element file.\n");
		return 1;
	}
	//forEach(elements, (void(*)(void *)) printElement);


	//printf("\nMagias:\n");
	Array * magics = loadMagics(magicFile, elements);
	if (magics == NULL) {
		printf("\nNo such magic file.\n");
		return 1;
	}
	//forEach(magics, (void(*)(void *)) printMagic);

	if (helpDialog) {
		char ESC = 27;
		printf("%c[1m", ESC); /*- turn on bold */
		printf("NOMBRE\n");
		printf("%c[0m", ESC); /* turn off bold */
		printf("\talquimia - console game\n");
		printf("%c[1m", ESC); /*- turn on bold */
		printf("DESCRIPCION\n");
		printf("%c[0m", ESC); /* turn off bold */
		printf(
				"Alquimia es un juego en el cual a partir de un grupo basico de elementos alquimicos dados se deben formar y descubrir los elementos restantes. Por ejemplo, si el usuario combina fuego (fire) con agua (water) obtiene vapor (steam).\n\tfire + water = steam\n");
		printf("%c[1m", ESC); /*- turn on bold */
		printf("OPCIONES\n");
		printf("%c[0m", ESC); /* turn off bold */
		printf( 
				"\nTodos los parametros de mas de una palabra se pasan entre comillas como si fuera un string.\n");
		printf(
				"\t-e <elemento>\n\t\t Dado un elemento, muestra todos los elementos que\n\t\tpueden llegar a ser formados por el mismo.\n");
		printf(
				"\t-t\n\t\t Muestra los elementos terminales: Los elementos\n\t\ta partir de los cuales no se puede obtener ning\n\t\tun otro.\n");
		printf(
				"\t-b <elemento>\n\t\t Dado un elemento, muestra todos los elementos bas\n\t\ticos que se utilizan para todas sus combinaciones \n\t\tque lo generan. \n");
		printf("\t-h\n\t\t Muestra este mensaje de ayuda.\n");
		printf(
				"\t-n <archivo>\n\t\t Archivo de donde tomar los nombres de los elementos.\n");
		printf(
				"\t-m <archivo>\n\t\t Archivo de donde tomar las combinaciones de los elementos.\n");
		printf("%c[1m", ESC); /*- turn on bold */
		printf("ELEMENTOS BASE\n");
		printf("%c[0m", ESC); /* turn off bold */
		Array * baseElements = getElementsByFunction(elements, (int(*)(
				void * e, void * arg)) isBasic, magics);
		bubleSort(baseElements, (int(*)(void *, void *)) elementAlfabeticCmp);
		forEach(baseElements, (void(*)(void *)) printElement);
	}

	if (rootElement) {
		if (rootElementARG != NULL && contains(elements, newElement(
				rootElementARG), (int(*)(void *, void *)) elementCmp)) {
			printf("\nElements done using %s:\n", rootElementARG);
			Array * elementsDone = getElementsDoneUsing(rootElementARG, magics);
			bubleSort(elementsDone,
					(int(*)(void *, void *)) elementAlfabeticCmp);
			forEach(elementsDone, (void(*)(void *)) printElement);
		} else {
			printf("\nNo such element %s\n", rootElementARG == NULL ? "(null)"
					: rootElementARG);
		}
	}

	if (terminalElements) {
		printf("\nTerminal elements:\n");
		Array * terminals = getElementsByFunction(elements, (int(*)(void *,
				void *)) isTerminal, magics);
		bubleSort(terminals, (int(*)(void *, void *)) elementAlfabeticCmp);
		forEach(terminals, (void(*)(void *)) printElement);
	}

	if (possibleCombinations) {
		if (possibleCombinationsARG != NULL && contains(elements, newElement(
				possibleCombinationsARG), (int(*)(void *, void *)) elementCmp)) {
			printf("\nBasic elements used for %s:\n", possibleCombinationsARG);
			Array * basics = getBasicsFor(possibleCombinationsARG, magics);
			bubleSort(basics, (int(*)(void *, void *)) elementAlfabeticCmp);
			forEach(basics, (void(*)(void *)) printElement);
		} else {
			printf("\nNo such element %s\n",
					possibleCombinationsARG == NULL ? "(null)"
							: possibleCombinationsARG);
		}
	}

	char ESC = 27;
	printf("%c[1m", ESC); /*- turn on bold */
	printf(
			"Game has started! \nEnter a magic combination <element>+<element> to get a result.\n");
	printf("%c[0m", ESC); /* turn off bold */

	while (1) {
		Magic * m = parseMagic();

		if (m != NULL) {
			Array * possible = getElementsByFunction(magics, (int(*)(void *,
					void *)) pseudoMagicCmp, m);

			if (possible->count > 0){
				printf("%c[1m", ESC); /*- turn on bold */
				forEach(possible, (void(*)(void *)) printMagicOutput);
				printf("%c[0m", ESC); /* turn off bold */
			}
			else
				printf("%c[1m???%c[0m\n", ESC, ESC);

		} else {
			printf("Wrong input. Returning to console...\n");
			return -1;
		}
	}

	return 0;
}
